#ifndef _GTL_STORAGE_HEADER_
#define _GTL_STORAGE_HEADER_

#include "sys/typedef.h"
#include "sys/error.h"
#include "IFileStream.h"

#define VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(simpleType)  \
	namespace gtl \
	{ \
		template<> \
		class ArchiveTypeInOutOperator< simpleType > :  \
			public SimpleDirectMemoryArchiveTypeInOutOperator< simpleType > \
		{ \
		}; \
	} \


#define VDEFINE_BYTEMEMORY_EXCHANGE(simpleType)  \
	namespace gtl \
	{ \
		template<> \
		class ArchiveTypeInOutOperator< simpleType > :  \
			public ByteMemoryArchiveTypeInOutOperator< simpleType > \
		{ \
		}; \
	} \


#define VDEFINE_BOOLMEMORY_EXCHANGE(simpleType)  \
	namespace gtl \
	{ \
		template<> \
		class ArchiveTypeInOutOperator< simpleType > :  \
			public BooleanMemoryArchiveTypeInOutOperator< simpleType > \
		{ \
		}; \
	} \


namespace gtl
{

template<class TYPE>
class ArchiveTypeInOutOperator;

template<class TYPE>
class SimpleDirectMemoryArchiveTypeInOutOperator
{
public:
	static void Save(IFileStream* pStream,const TYPE& v)
	{
		long r;
		pStream->SaveData(reinterpret_cast<const byte*>(&v), sizeof(TYPE),&r);
		ASSERT(r==sizeof(v));
	}
	static void Load(IFileStream* pStream,TYPE& v)
	{
		long r;
		pStream->LoadData(reinterpret_cast<byte*>(&v), sizeof(TYPE),&r);
		ASSERT(r==sizeof(v));
	}
};

template<class TYPE>
class ByteMemoryArchiveTypeInOutOperator
{
public:
	static void Save(IFileStream* pStream,const TYPE& v)
	{
		byte vb = static_cast<byte>(v);
		long r;
		pStream->SaveData(reinterpret_cast<const byte*>(&vb), sizeof(byte),&r);
		ASSERT(r==sizeof(vb));
	}
	static void Load(IFileStream* pStream,TYPE& v)
	{
		byte vb; 
		long r;
		pStream->LoadData(reinterpret_cast<byte*>(&vb), sizeof(byte),&r);
		ASSERT(r==sizeof(vb));
		v = static_cast<TYPE>(vb);
	}
};

template<class TYPE>
class BooleanMemoryArchiveTypeInOutOperator
{
public:
	static void Save(IFileStream* pStream,const TYPE& v)
	{
		byte vb = v ? 1 : 0;
		long r;
		pStream->SaveData(reinterpret_cast<const byte*>(&vb), sizeof(byte),&r);
		ASSERT(r==sizeof(vb));
	}
	static void Load(IFileStream* pStream,TYPE& v)
	{
		byte vb; 
		long r;
		pStream->LoadData(reinterpret_cast<byte*>(&vb), sizeof(byte),&r);
		ASSERT(r==sizeof(vb));
		v = (vb != 0) ? true : false;
	}
};

}

VDEFINE_BOOLMEMORY_EXCHANGE(bool)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(byte)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(char)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(uint16)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(short)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(uint32)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(long)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(ulong)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(int32)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(real4)
VDEFINE_SIMPLEDIRECTMEMORY_EXCHANGE(real8)

/************************************************************************
howto define your save method?

namespace gtl
{
template<>
class ArchiveTypeInOutOperator<YourSpecialType>
{
public:
	static void Save(IFileStream* pStream,const YourSpecialType& v)
	{
		//...
	}
	static void Load(IFileStream* pStream,YourSpecialType& v)
	{
		//...
	}
};
}
************************************************************************/


namespace gtl
{

const uint16 SECTION_VERSION_ENDFLAG(static_cast<uint16>(0xFFFFu));

class ArchiveAssist
{
public:
	explicit ArchiveAssist(bool bLoad,IFileStream* pStream,uint16 wCurrentProgramVersion) :
			m_pStream(pStream),
			m_bLoadingArchive(bLoad),
			m_wCurrentProgramVersion(wCurrentProgramVersion),
			m_wReadVersion(0),
			m_wArchiveVersion(wCurrentProgramVersion),
			m_bNoStartSave(true),
			m_vrCode(vok),
			m_wReadVerMustLessThan(0),
			m_bSectionDealt(true)
		{
			ASSERT(pStream);
			ASSERT(wCurrentProgramVersion>0);
			if((bLoad && !pStream->CanLoad()) || (!bLoad&& !pStream->CanSave()))
				m_vrCode=err_invalid;
		}
private:
	ArchiveAssist(const ArchiveAssist&);
	void operator=(ArchiveAssist&);
public:
	template<class TYPE>
	inline void Save(const TYPE& v)
		{
			ASSERT(!m_bLoadingArchive);
			ArchiveTypeInOutOperator<TYPE>::Save(m_pStream,v);
		}
	template<class TYPE>
	inline void Load(TYPE& v)
		{
			ASSERT(m_bLoadingArchive);
			ArchiveTypeInOutOperator<TYPE>::Load(m_pStream,v);
		}
	template<class TYPE>
	inline void Exchange(TYPE& v)
		{
			if(m_bLoadingArchive)
			{
				Load(v);
			}
			else
			{
				Save(v);
			}
		}
	template<class TYPE>
	inline void SaveAsString(const TYPE& v)
		{
			ASSERT(!m_bLoadingArchive);
			const std::string& s=a2b<std::string>::conv(v);
			long r;
			m_pStream->SaveData(reinterpret_cast<const byte*>(s.c_str()),s.length(),&r);
			ASSERT((std::string::size_type)r==s.length());
		}
	inline bool IsLoading()const
		{
			return m_bLoadingArchive;
		}
public:
	template<class TYPE>
	inline ArchiveAssist& operator<<(const TYPE& v)
		{
			Save(v);
			return *this;
		}
	template<class TYPE>
	inline ArchiveAssist& operator>>(TYPE& v)
		{
			Load(v);
			return *this;
		}
public:
	inline vresult GetResultCode()const
		{
			return m_vrCode;
		}
	inline uint16 GetArchiveVersion()const
	{
		return m_wArchiveVersion;
	}
	int32 DealObjectVer()
	{
		//---verify...
		//ASSERT(m_wCurrentProgramVersion == 1);

		if(m_vrCode!=vok)
			return -1;

		if(m_bLoadingArchive)
		{		
			Load(m_wReadVersion);
			if(m_wReadVersion<=0)
			{
				SetErrorCodeAndReturnFalse(err_zeroversion);
				return -1;
			}
			if(m_wReadVersion>m_wCurrentProgramVersion)
			{
				SetErrorCodeAndReturnFalse(err_higherversion);
				return -1;
			}
			m_wArchiveVersion = m_wReadVersion;
			return m_wReadVersion;
		}
		else
		{
			Save(m_wCurrentProgramVersion);
			return	m_wCurrentProgramVersion;
		}
	}
	bool DealRemainSection()
		{
			if(m_vrCode!=vok)
				return false;

			if(m_bLoadingArchive)
			{
				if(!m_bSectionDealt)
					return SetErrorCodeAndReturnFalse(err_section);
				Load(m_wReadVersion);
				if(m_wReadVersion==SECTION_VERSION_ENDFLAG)
					return false;
				if(m_wReadVersion==0)
					return SetErrorCodeAndReturnFalse(err_zeroversion);
				if(m_wReadVersion>m_wCurrentProgramVersion)
					return SetErrorCodeAndReturnFalse(err_higherversion);
				if(m_wReadVerMustLessThan>0 && m_wReadVersion>=m_wReadVerMustLessThan)
					return SetErrorCodeAndReturnFalse(err_versionerror);
				m_bSectionDealt=false;
				m_wArchiveVersion = m_wReadVersion;
				return true;
			}
			else
			{
				if(m_bNoStartSave)
				{
					m_bNoStartSave=false;
					return true;
				}
				Save(SECTION_VERSION_ENDFLAG);
				return false;
			}
		}
	bool DealSection(uint16 wSectionVersion)
		{
			ASSERT(0<wSectionVersion && wSectionVersion<=m_wCurrentProgramVersion);

			if(m_vrCode!=vok)
				return false;
			if(m_bLoadingArchive)
			{
				if(m_wReadVersion==wSectionVersion)
				{
					m_bSectionDealt=true;
					return true;
				}
				return false;
			}
			else
			{
				//ASSERT(m_wCurrentProgramVersion == wSectionVersion);
				Save(wSectionVersion);
				return true;
			}
		}
	bool DealRemovedSection(uint16 wSectionVersion,uint16 wDelVersion)
		{
			ASSERT(0<wSectionVersion);
			ASSERT(wSectionVersion<wDelVersion);
			ASSERT(wDelVersion<=m_wCurrentProgramVersion);

			if(m_vrCode!=vok)
				return false;
			
			if(m_bLoadingArchive)
			{
				if(m_wReadVersion==wSectionVersion)
				{
					if(wDelVersion>m_wReadVerMustLessThan)
						m_wReadVerMustLessThan=wDelVersion;
					m_bSectionDealt=true;
					return true;
				}
			}
			return false;
		}
private:
	bool SetErrorCodeAndReturnFalse(vresult vr)
		{
			m_vrCode=vr;
			return false;
		}
private:
	IFileStream* const	m_pStream;
	const bool			m_bLoadingArchive;
	const uint16			m_wCurrentProgramVersion;

	bool		m_bNoStartSave;
	vresult		m_vrCode;
	bool		m_bSectionDealt;
	uint16		m_wReadVersion;
	uint16		m_wReadVerMustLessThan;
	uint16		m_wArchiveVersion;
};
}

#endif